﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.IO;

namespace Genuilder.Extensibility.Tests
{
	public class MockCodeItemMementoRepository : CodeItemMementoRepository
	{
		IEnumerable<DirectoryCodeRepository.CodeItemMemento> _Mementos = new List<DirectoryCodeRepository.CodeItemMemento>();
		#region CodeItemMementoRepository Members

		public IEnumerable<DirectoryCodeRepository.CodeItemMemento> LoadCodeItemMementos()
		{
			return _Mementos;
		}

		public void SaveCodeItemMementos(IEnumerable<DirectoryCodeRepository.CodeItemMemento> mementos)
		{
			_Mementos = mementos.ToList();
		}

		#endregion
	}


	public class CodeItemTester
	{
		private readonly string _FileName;
		public string FileName
		{
			get
			{
				return _FileName;
			}
		}
		public CodeItemTester(string fileName)
		{
			if(fileName == null)
				throw new ArgumentNullException("fileName");
			_FileName = fileName;
		}
		public void Listen(ICodeRepository repository)
		{
			repository.CodeItemCreated += new CodeItemCreatedHandler(repository_CodeTracked);
		}

		void repository_CodeTracked(CodeItem item)
		{
			if(item.Name == FileName)
			{
				_Tracked = true;
				item.ContentChanged += new ContentChangedHandler(item_ContentChanged);
				item.StateChanged += new StateChangedHandler(item_StateChanged);
			}
		}

		void item_StateChanged(CodeItem sender, CodeItemState newState, CodeItemState oldState)
		{
			_NewState = newState;
		}

		void item_ContentChanged(CodeItem sender)
		{
			_Modified = true;
		}


		bool _Tracked;
		bool _Modified;
		CodeItemState? _NewState;

		public bool? AssertModified
		{
			get;
			set;
		}

		public bool? AssertTracked
		{
			get;
			set;
		}

		public bool? AssertStateModified
		{
			get;
			set;
		}
		public CodeItemState? AssertNewState
		{
			get;
			set;
		}

		public void CheckAsserts()
		{
			if(AssertTracked.HasValue)
			{
				Assert.AreEqual(AssertTracked.Value, _Tracked);
			}
			if(AssertModified.HasValue)
			{
				Assert.AreEqual(AssertModified.Value, _Modified, "Modification Test Failed");
			}
			if(AssertStateModified.HasValue)
			{
				Assert.AreEqual(AssertStateModified.Value, _NewState.HasValue);
			}
			if(AssertNewState.HasValue)
			{
				Assert.AreEqual(AssertNewState.Value, _NewState.Value);
			}
		}
	}

	[TestClass]
	public class DirectoryCodeRepositoryTest
	{
		[TestMethod]
		public void ShouldTrackNewFile()
		{
			var testFileName = CreateFile("test1");
			var tester = new CodeItemTester(testFileName);
			tester.AssertTracked = true;
			tester.AssertModified = true;
			tester.AssertStateModified = false;
			var repository = CreateDirectoryCodeRepository();
			repository.Files.Add(testFileName);
			tester.Listen(repository);
			repository.Initialize();
			tester.CheckAsserts();
		}

		[TestMethod]
		public void ShouldTrackExistingFile()
		{
			var testFileName = CreateFile("test2");
			var tester = new CodeItemTester(testFileName);
			tester.AssertTracked = true;
			tester.AssertModified = false;
			tester.AssertStateModified = false;
			var repository = CreateDirectoryCodeRepository();
			repository.Files.Add(testFileName);
			repository.Initialize();
			tester.Listen(repository);
			repository.Initialize();
			tester.CheckAsserts();
		}

		[TestMethod]
		public void ShouldDetectModifiedFile()
		{
			var testFileName = CreateFile("test2");
			var tester = new CodeItemTester(testFileName);
			tester.AssertTracked = true;
			tester.AssertModified = true;
			tester.AssertStateModified = false;
			var repository = CreateDirectoryCodeRepository();
			repository.Files.Add(testFileName);
			repository.Initialize();
			tester.Listen(repository);
			File.WriteAllText(testFileName, "content");
			repository.Initialize();
			tester.CheckAsserts();
		}

		[TestMethod]
		public void ShouldDetectDeletedFile()
		{
			var testFileName = CreateFile("test2");
			var tester = new CodeItemTester(testFileName);
			tester.AssertTracked = true;
			tester.AssertModified = false;
			tester.AssertStateModified = true;
			tester.AssertNewState = CodeItemState.Deleted;
			var repository = CreateDirectoryCodeRepository();
			repository.Files.Add(testFileName);
			repository.Initialize();
			tester.Listen(repository);
			File.Delete(testFileName);
			repository.Initialize();
			tester.CheckAsserts();
		}

		private static DirectoryCodeRepository CreateDirectoryCodeRepository()
		{
			var mementoRepository = new MockCodeItemMementoRepository();
			var repository = new DirectoryCodeRepository(mementoRepository);
			return repository;
		}

		[TestMethod]
		public void ShouldDetectExcludedFile()
		{
			var testFileName = CreateFile("test2");
			var tester = new CodeItemTester(testFileName);
			tester.AssertTracked = true;
			tester.AssertModified = false;
			tester.AssertStateModified = true;
			tester.AssertNewState = CodeItemState.Excluded;
			var repository = CreateDirectoryCodeRepository();
			repository.Files.Add(testFileName);
			repository.Initialize();
			tester.Listen(repository);
			repository.Files.Remove(testFileName);
			repository.Initialize();
			tester.CheckAsserts();
		}


		private string CreateFile(string fileName)
		{
			File.Create(fileName).Close();
			return fileName;
		}
	}
}
